

import PyQt4
from PyQt4.QtCore import Qt, QString, QStringList, QModelIndex

IS_MAC = hasattr(PyQt4.QtGui, "qt_mac_set_native_menubar")
eolChar = "\n"
if IS_MAC: eolChar = "\r"

class SCTableReadTools(object):
    '''A series of functions for reading and formatting
    cell data.
    '''
    
    @staticmethod
    def allToText(dataModel,evaluate=False):
        '''Reads all the cells and creates a string
        '''
        rows, cols = dataModel.rowCount(), dataModel.columnCount()
        return SCTableReadTools.rectToText(dataModel,0,rows-1,0,cols-1,evaluate)
        
    @staticmethod
    def rectToText(dataModel,rowMin,rowMax,colMin,colMax,evaluate=False):
        '''Reads text contained in the rectangle (rowMin,rowMax,colMin,colMax).
        '''
        rows, cols = rowMax-rowMin+1, colMax-colMin+1
        stringList = [None,]*rows
        for i in range(rows):
            tmpRow = [None,]*cols
            for j in range(cols):
                index = dataModel.index(rowMin+i,colMin+j,QModelIndex())
                if evaluate:
                    tmpRow[j] = dataModel.data(index,Qt.UserRole).toString()
                else:
                    tmpRow[j] = dataModel.data(index,Qt.EditRole).toString()
            stringList[i] = QStringList(tmpRow).join('\t')
        return QStringList(stringList).join('\n')
    
    @staticmethod
    def rangeToText(dataModel,cellRange,evaluate=False):
        '''Transforms the contents of a cellRange into a string.
        '''
        rowMin, rowMax, colMin, colMax = SCTablePositionTools.getRect(cellRange)
        return SCTableReadTools.rectToText(dataModel,rowMin,rowMax,colMin,colMax,evaluate)
    
    @staticmethod
    def selectionToText(parentWidget,evaluate=False):
        '''Helper function to put selection contents to a string.
        '''
        dataModel = parentWidget.model()
        cellRange = parentWidget.selectionModel().selectedIndexes()
        return SCTableReadTools.rangeToText(dataModel,cellRange,evaluate)

    @staticmethod
    def simplifyShape(alist, recursion=0):
        """Reduce the alist dimension if needed"""
        if not isinstance(alist,(tuple,list)):
            return alist
        if recursion != 0:
            if len(alist) == 1:
                return alist[-1]
            return alist
        if len(alist) == 1:
            return SCTableReadTools.simplifyShape(alist[-1], 1)
        return map(lambda al: SCTableReadTools.simplifyShape(al, 1), alist)

class SCTableWriteTools(object):
    
    @staticmethod
    def checkSpace(data,dataModel,startIndex,writeDir='both'):
        if writeDir == 'horiz':
            return SCTableWriteTools.__checkHorizSpace__(data, dataModel, startIndex)
        elif writeDir == 'vert':
            return SCTableWriteTools.__checkVertSpace__(data, dataModel, startIndex)
        elif writeDir == 'both':
            return SCTableWriteTools.__checkBothSpace__(data, dataModel, startIndex)
        raise Exception('Wrong write direction.')
    
    @staticmethod
    def __getDataSize__(data):
        nRows, nCols = 0, 0
        if hasattr(data,'__iter__'):
            nRows = len(data)
            if hasattr(data[0],'__iter__'):
                nCols = len(data[0])
        return nRows, nCols

    @staticmethod
    def __checkHorizSpace__(data,dataModel,startIndex):
        nDim = max(*SCTableWriteTools.__getDataSize__(data))
        dispCols = dataModel.columnCount()-startIndex.column()
        assert nDim <= dispCols, \
            "Table not big enough! Please add %d columns." % max(0,nDim-dispCols)
    
    @staticmethod
    def __checkVertSpace__(data,dataModel,startIndex):
        nDim = max(*SCTableWriteTools.__getDataSize__(data))
        dispRows = dataModel.rowCount()-startIndex.row()
        assert nDim <= dispRows, \
            "Table not big enough! Please add %d rows." % max(0,nDim-dispRows)

    @staticmethod
    def __checkBothSpace__(data,dataModel,startIndex):
        nRows, nCols = SCTableWriteTools.__getDataSize__(data)
        dispRows = dataModel.rowCount()-startIndex.row()
        dispCols = dataModel.columnCount()-startIndex.column()
        assert nRows <= dispRows and nCols <= dispCols, \
            'Table not big enough! Please add %d rows and %d columns.' % \
            (max(0,nRows-dispRows), max(0,nCols-dispCols))        
    
class SCTablePositionTools(object):
    '''Some tools for handling shapes, relative and
    absolute positions.
    '''
    
    @staticmethod
    def getDimension(modIndexList):
        rows = [i.row() for i in modIndexList]
        cols = [i.column() for i in modIndexList]
        rowMin, rowMax = min(rows), max(rows)
        colMin, colMax = min(cols), max(cols)
        if rowMin == rowMax and colMin == colMax:
            return 0
        elif rowMin != rowMax and colMin != colMax:
            return 2
        elif rowMin != rowMax or colMin != colMax:
            return 1
        else: raise Exception("Wrong dim.")

    @staticmethod
    def getShape(modIndexList):
        rows = [i.row() for i in modIndexList]
        cols = [i.column() for i in modIndexList]
        rowMin, rowMax = min(rows), max(rows)
        colMin, colMax = min(cols), max(cols)
        return (rowMax-rowMin+1,colMax-colMin+1)

    @staticmethod
    def getRect(modIndexList):
        rows = [i.row() for i in modIndexList]
        cols = [i.column() for i in modIndexList]
        rowMin, rowMax = min(rows), max(rows)
        colMin, colMax = min(cols), max(cols)
        return (rowMin,rowMax,colMin,colMax)

from datetime import date,datetime

class SCTableGenerationTool(object):
    
    @staticmethod
    def generateValues(dataModel,modIndexList):
        '''Generate values based on first two.'''
        assert SCTablePositionTools.getDimension(modIndexList) == 1, \
            "Selection must be one-dimensional."
        assert len(modIndexList) > 2, 'Please select more than 2 cells.'
        val0 = dataModel.evalCell(modIndexList[0].row(),modIndexList[0].column())
        val1 = dataModel.evalCell(modIndexList[1].row(),modIndexList[1].column())
        assert val1 is not None, \
            'Please assign the first two values of the selected series.'
        diff, val = val1 - val0, val1
        for idx in modIndexList[2:]:
            val += diff
            if isinstance(val0,int):
                val = int(val)
            dataModel.setData(idx,val)            

    @staticmethod
    def fillInValues(dataModel,modIndexList):
        '''Generate values based on first and last.'''
        assert SCTablePositionTools.getDimension(modIndexList) == 1, \
            "Selection must be one-dimensional."
        assert len(modIndexList) > 2, 'Please select more than 2 cells.'
        val0 = dataModel.evalCell(modIndexList[0].row(),modIndexList[0].column())
        valN = dataModel.evalCell(modIndexList[-1].row(),modIndexList[-1].column())
        assert valN is not None, \
            'Please assign the first and the last value of the selected series.'
        nCells = max(*SCTablePositionTools.getShape(modIndexList))-1
        if not isinstance(val0,(date,datetime)):
            nCells = float(nCells)
        diff, val = (valN - val0)/nCells, val0
        for idx in modIndexList[1:-1]:
            val += diff
            if isinstance(val0,int):
                val = int(val)
            dataModel.setData(idx,val)
    
    @staticmethod
    def repeatValue(dataModel,modIndexList):
        '''Repeat the first value on all selection.'''
        val0 = dataModel.evalCell(modIndexList[0].row(),modIndexList[0].column())
        for idx in modIndexList[1:]:
            dataModel.setData(idx,val0)

    @staticmethod
    def insertToday(dataModel,index):
        '''Insert today on the selected cell'''
        today = datetime.now().date()
        dataModel.setData(index,today)
